home *** CD-ROM | disk | FTP | other *** search
-
- /*
- * (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved.
- *
- * (b) If this Sample Code is distributed as part of the Display PostScript
- * System Software Development Kit from Adobe Systems Incorporated,
- * then this copy is designated as Development Software and its use is
- * subject to the terms of the License Agreement attached to such Kit.
- *
- * (c) If this Sample Code is distributed independently, then the following
- * terms apply:
- *
- * (d) This file may be freely copied and redistributed as long as:
- * 1) Parts (a), (d), (e) and (f) continue to be included in the file,
- * 2) If the file has been modified in any way, a notice of such
- * modification is conspicuously indicated.
- *
- * (e) PostScript, Display PostScript, and Adobe are registered trademarks of
- * Adobe Systems Incorporated.
- *
- * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
- * CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
- * AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
- * ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
- * OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
- * WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
- * WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
- * DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
- * OF THIRD PARTY RIGHTS.
- */
-
- /*
- * Document.m
- *
- * Portions of the source code in this file are based on source code
- * from the Draw example application provided by NeXT.
- *
- * The Document class serves to keep track of the global information
- * concerning a particular file. It sets up the window and the views
- * (ScrollView, DocView and DrawingView) . It manages the name
- * of the file as well as save and print info information.
- *
- * The listenerId is used to allow the user to drag an icon representing
- * a PostScript file into the document. The iconPathList is the
- * list of files which was last dragged into the document.
- *
- * The saved variable marks whether a disk file has been
- * created for the document (i.e. if it has ever been saved).
- *
- * Version: 2.0
- * Author: Ken Fromm
- */
-
- #import "Document.h"
- #import "ImportApp.h"
- #import "DocView.h"
- #import "DrawingView.h"
- #import "ScrollingView.h"
- #import "ImportPanel.h"
- #import "SaveAsPanel.h"
-
- #import <appkit/Cursor.h>
- #import <appkit/Listener.h>
- #import <appkit/Matrix.h>
- #import <appkit/PageLayout.h>
- #import <appkit/PrintInfo.h>
- #import <appkit/ScrollView.h>
- #import <appkit/Speaker.h>
- #import <appkit/nextstd.h>
- #import <appkit/publicWraps.h>
- #import <objc/hashtable.h>
- #import <string.h>
-
- const NXRect DefaultContentRect = {0.0, 0.0, 575.0, 660.0};
- static const char DefaultName[] = "Empty Window";
-
- extern void resizeBuffer();
-
- @implementation Document
-
- /* Factory methods */
-
- /*
- * Creates a new, empty, document.
- *
- * Creates a PrintInfo object; creates a view whose size depends on the
- * default PrintInfo created; creates a window for that view; sets self
- * as the window's delegate; orders the window front; registers the window
- * with the Workspace Manager. The default margins are set to 1/4 inch.
- */
- + new
- {
- self = [super new];
-
- printinfoId = [PrintInfo new];
- [printinfoId setMarginLeft:18.0 right:18.0 top:18.0 bottom:18.0];
-
- [self createWindow];
- [self setName:NULL andDirectory:NULL];
- [self setDocument];
-
- return self;
- }
-
- /* Opens an existing document from the specified file. */
- + newFromFile:(const char *)file
- {
- NXStream *stream;
-
- self = [super new];
- stream = NXMapFile(file, NX_READONLY);
-
- [self createWindow];
- if ([self readFromStream:stream])
- {
- [self setName:file];
- [self setDocument];
- }
- else
- {
- [self free];
- self = nil;
- }
-
- return self;
- }
-
- /* Very private instance method needed by factory methods */
-
- /*
- * This method loads an archived document from the
- * specified file name. The frame size is read first and then
- * the window is displayed before the rest of the document
- * is loaded (the print info and the drawing view).
- *
- * Does not place newView as the window's drawing view until
- * the read is complete. If a failure occurs for some reason
- * during the read, the previous drawing view is retained.
- *
- * An NX_DURING handler is needed around the NXTypedStream
- * operations because if the user has asked that a bogus file be
- * opened, the NXTypedStream will raise an error. To handle the
- * error, the NXTypedStream must be closed.
- */
- - readFromStream:(NXStream *)stream
- {
- BOOL err = YES;
-
- NXTypedStream *volatile ts = NULL;
-
- if (stream)
- {
- NX_DURING
- ts = NXOpenTypedStream(stream, NX_READONLY);
- if (ts)
- {
- printinfoId = NXReadObject(ts);
- drawingviewId = NXReadObject(ts);
- NXCloseTypedStream(ts);
- err = NO;
- }
- NX_HANDLER
- NXCloseTypedStream(ts);
- NX_ENDHANDLER
-
- NXClose(stream);
- }
-
- if (!err)
- {
- [[docviewId setDrawView:drawingviewId] free];
- [docviewId placeView:drawingviewId];
- [[windowId contentView] display];
- }
- else
- Notify("Open Error", "Cannot open file.");
-
- return self;
- }
-
- - setDocument
- {
- NXPoint location;
-
- NXRect winFrame;
-
- [docviewId placeView:drawingviewId];
-
- [self registerWindow];
- [windowId setDelegate:self];
- [windowId display];
-
- [windowId getFrame:&winFrame];
- [NXApp getPosition:&location forSize:&winFrame.size];
- [windowId moveTo:location.x :location.y];
-
- [windowId makeKeyAndOrderFront:self];
-
- return self;
- }
-
- - free
- {
- [printinfoId free];
- [windowId free];
-
- NX_FREE(name);
- NX_FREE(directory);
- NX_FREE(iconPathList);
-
- return [super free];
- }
-
- /*
- * Create the drawing window and place a scrollview as the content view.
- * A DrawingView instance is placed as the DocView of the ScrollView.
- */
- - createWindow
- {
- id scrollView;
-
- const NXRect *paperRect;
-
- windowId = [Window newContent:&DefaultContentRect
- style:NX_TITLEDSTYLE
- backing:NX_BUFFERED
- buttonMask:NX_ALLBUTTONS
- defer:NO];
- [windowId addToEventMask:NX_FLAGSCHANGEDMASK];
-
- scrollView = [ScrollingView newFrame:&DefaultContentRect];
- [scrollView setBorderType:SCROLLVIEW_BORDER];
-
- paperRect = [printinfoId paperRect];
- drawingviewId = [DrawingView newFrame:paperRect];
- docviewId = [[[[DocView new] setClipping:NO] setScale:1.0] setFlipped:NO];
- [docviewId notifyAncestorWhenFrameChanged:YES];
-
- [docviewId setDrawView:drawingviewId];
- [scrollView setDocView:docviewId];
- [[docviewId superview] allocateGState];
-
- [[windowId setContentView:scrollView] free];
-
- [windowId makeFirstResponder:drawingviewId];
-
- return self;
- }
-
- - window
- {
- return windowId;
- }
-
- /* Returns the DrawingView associated with this document. */
- - drawingView
- {
- return drawingviewId;
- }
-
- - docView
- {
- return docviewId;
- }
-
- - printInfo
- {
- return printinfoId;
- }
-
- /* Target/Action methods */
- /*
- * Puts up a PageLayout panel and allows the user to pick a different
- * size paper to work on. The view is then resized to the
- * new paper size. The new DrawingView is placed in the DocView
- * and then scrolled to the previous rectangle.
- * The view is dirtied because the PrintInfo is part of the document.
- */
- - changeLayout:sender
- {
- NXRect visibleRect;
- const NXRect *paperRect;
-
- if ([[PageLayout new] runModal] == NX_OKTAG)
- {
- [drawingviewId getVisibleRect:&visibleRect];
- paperRect = [printinfoId paperRect];
- [drawingviewId sizeTo:paperRect->size.width :paperRect->size.height];
- [docviewId placeView:drawingviewId];
- [drawingviewId scrollRectToVisible:&visibleRect];
- [[windowId contentView] display];
- [drawingviewId setDirty:YES];
- }
-
- return self;
- }
-
- - print:sender
- {
- return [drawingviewId printPSCode:sender];
- }
-
- /* Revert the document back to what is on the disk. */
- - revertToSaved:sender
- {
- NXStream *stream;
- NXRect visibleRect;
- const NXRect *paperRect;
-
- if (!saved || ![drawingviewId isDirty] ||
- (NXRunAlertPanel("Revert",
- "%s has been edited. Are you sure you want to undo changes?",
- "Revert", "Cancel", NULL, name) != NX_ALERTDEFAULT))
- {
- return self;
- }
-
- [drawingviewId getVisibleRect:&visibleRect];
- [windowId endEditingFor:self];
-
- stream = NXMapFile([self filename], NX_READONLY);
- if (stream && [self readFromStream:stream])
- {
- [windowId disableDisplay];
- paperRect = [printinfoId paperRect];
- [drawingviewId sizeTo:paperRect->size.width :paperRect->size.height];
- [docviewId placeView:drawingviewId];
- [drawingviewId scrollRectToVisible:&visibleRect];
- [windowId reenableDisplay];
- [[windowId contentView] display];
- [windowId makeFirstResponder:drawingviewId];
- [drawingviewId setDirty:NO];
- NXClose(stream);
- }
- else
- {
- if (stream)
- NXClose(stream);
- Notify("Revert Error", "Cannot revert to saved file.");
- }
-
- return self;
- }
-
- /*
- * Bring up the import (open) panel to obtain the file and then pass
- * to the drawing view.
- */
-
- - import:sender
- {
- id importpanel;
-
- static const char *const filetype[4] = {"ps", "eps", "tiff", NULL};
-
- importpanel = [[ImportPanel new] setImport];
- if ([importpanel runModalForTypes:filetype])
- [drawingviewId importFile:[importpanel filename] at:NULL];
-
- return self;
- }
-
- /*
- * Writes out the document in archive format.
- * (Saves the PrintInfo and DrawingView objects. See
- * DrawingView's write: methods for more details.)
- */
- - saveFile:(const char *) file
- {
- BOOL error;
-
- NXTypedStream *typedstream;
-
- error = YES;
- typedstream = NXOpenTypedStreamForFile(file, NX_WRITEONLY);
- if (typedstream)
- {
- NXWriteRootObject(typedstream, printinfoId);
- NXWriteRootObject(typedstream, drawingviewId);
- NXCloseTypedStream(typedstream);
- error = NO;
- }
- else
- Notify("Save Error", "Cannot open a typed stream to the file.");
-
- return (error ? nil : self);
- }
-
- /*
- * Saves the file. If this document has never been saved to disk,
- * then a SavePanel is put up to ask the user what file name she
- * wishes to use to save the document.
- */
- - save:sender
- {
- id savepanel;
-
- if ([drawingviewId isDirty] || !saved)
- {
- if (!saved)
- {
- savepanel = [[SaveAsPanel new] setSave];
- if (![savepanel runModalForDirectory:directory file:NULL])
- return self;
-
- [self setName:[savepanel filename]];
- }
-
- if ([self saveFile:[self filename]])
- {
- [drawingviewId setDirty:NO];
- saved = YES;
- }
- }
-
- return self;
- }
-
- - saveAs:sender
- {
- id savepanel;
-
- char *tempname;
-
- if (strcmp(name, DefaultName) == 0)
- tempname = NULL;
- else
- tempname = name;
-
- savepanel = [[SaveAsPanel new] setSaveAs];
- if ([savepanel runModalForDirectory:directory file:tempname])
- {
- [drawingviewId setDirty:YES];
- [self setName:[savepanel filename]];
- if ([self saveFile:[self filename]])
- {
- [drawingviewId setDirty:NO];
- saved = YES;
- }
- }
-
- return self;
- }
-
- /*
- * Writes out the document in Epsf/Illustrator format.
- */
- - saveTo:sender
- {
- id savepanel;
-
- NXStream *stream;
-
- savepanel = [[SaveAsPanel new] setSaveTo];
- if ([savepanel runModal])
- {
- stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
- if (stream)
- {
- [drawingviewId writePSToStream:stream];
- NXSaveToFile(stream, [savepanel filename]);
- NXCloseMemory(stream, NX_FREEBUFFER);
- }
- else
- Notify("Save Error", "Cannot open a stream to the file.");
- }
-
- return self;
- }
-
- /* Methods related to naming/saving this document. */
- /*
- * Gets the fully specified file name of the document.
- */
- - (const char *)filename
- {
- static char filenamebuf[MAXPATHLEN+1];
-
- if (!directory || !name)
- [self setName:name andDirectory:directory];
-
- if (directory)
- {
- strcpy(filenamebuf, directory);
- strcat(filenamebuf, "/");
- }
- else
- filenamebuf[0] = '\0';
-
- if (name)
- strcat(filenamebuf, name);
-
- return filenamebuf;
- }
-
- - (const char *)directory
- {
- return directory;
- }
-
- - (const char *)name
- {
- return name;
- }
-
- /*
- * If file is a full path name, then both the name and directory of the
- * document is updated appropriately, otherwise, only the name is changed.
- */
- - setName:(const char *)file
- {
- char *lastComponent;
- char path[MAXPATHLEN+1];
-
- if (file)
- {
- strcpy(path, file);
- lastComponent = strrchr(path, '/');
- if (lastComponent)
- {
- *lastComponent++ = '\0';
- return [self setName:lastComponent andDirectory:path];
- }
- else
- return [self setName:file andDirectory:NULL];
- }
-
- return self;
- }
-
- /*
- * Updates the name and directory of the document.
- * newName or newDirectory can be NULL, in which case the name or directory
- * will not be changed (unless one is currently not set, in which case
- * a default name will be used).
- */
- - setName:(const char *)newName andDirectory:(const char *)newDirectory
- {
- if ((newName && *newName) || !name)
- {
- if (!newName || !*newName)
- newName = DefaultName;
- NX_FREE(name);
- name = NXCopyStringBuffer(newName);
- }
-
- if ((newDirectory && (*newDirectory == '/')) || !directory)
- {
- if (!newDirectory || (*newDirectory != '/'))
- newDirectory = [NXApp currentDirectory];
- NX_FREE(directory);
- directory = NXCopyStringBufferFromZone(newDirectory, [self zone]);
- }
- [windowId setTitleAsFilename:[self filename]];
-
- return self;
- }
-
- /* Window delegate methods. */
-
- /*
- * Switch the Application's PrintInfo to the document's when the document
- * window becomes the main window. Also set the cursor appropriately
- * depending on which tool is currently selected.
- */
- - windowDidBecomeMain:sender
- {
- [NXApp setPrintInfo:printinfoId];
- [self resetResponder];
-
- return self;
- }
-
- /*
- * Prevents the window from getting too large or too small.
- */
- - windowWillResize:sender toSize:(NXSize *)size
- {
- NXSize screenSize;
-
- [NXApp getScreenSize:&screenSize];
- screenSize.width = screenSize.width * 0.90;
- screenSize.height = screenSize.height * 0.90;
-
- size->width = MIN(screenSize.width, size->width);
- size->height = MIN(screenSize.height, size->height);
-
- size->width = MAX(MIN_WINDOW_WIDTH, size->width);
- size->height = MAX(MIN_WINDOW_HEIGHT, size->height);
-
- return self;
- }
-
- /*
- * Resizes the doc view and repositions the drawing view inside the doc view.
- */
- - windowDidResize:sender
- {
- NXRect frameRect, contRect;
-
- [windowId getFrame:&frameRect];
- [Window getContentRect:&contRect forFrameRect:&frameRect style:[windowId style]];
- resizeBuffer([drawingviewId buffer], &contRect.size);
-
- [docviewId placeView:drawingviewId];
-
- return self;
- }
-
- /*
- * If the GraphicView has been edited, then this asks the user if she
- * wants to save the changes before closing the window. When the window
- * is closed, the DrawDocument itself must be freed. This is accomplished
- * via Application's delayedFree: mechanism. Unfortunately, by the time
- * delayedFree: frees the DrawDocument, the window and view instance variables
- * will already have automatically been freed by virtue of the window's being
- * closed. Thus, those instance variables must be set to nil to avoid their
- * being freed twice.
- *
- * Returning nil from this method informs the caller that the window should
- * NOT be closed. Anything else implies it should be closed.
- */
- - windowWillClose:sender
- {
- int save;
-
- if ([drawingviewId isDirty] && (saved || ![drawingviewId isEmpty]))
- {
- save = NXRunAlertPanel("Close", "%s has changes. Save them?", "Save",
- "Don't Save", "Cancel", name);
- if (save == NX_ALERTDEFAULT || save == NX_ALERTALTERNATE)
- {
- [sender endEditingFor:self]; /* terminate any editing */
- if (save == NX_ALERTDEFAULT)
- [self save:nil];
- }
- else
- return nil;
- }
-
- [self unregisterWindow];
-
- [NXApp setPrintInfo:nil];
-
- return self;
- }
-
- /* Icon dragging methods */
-
- /*
- * Registers the document window with the Workspace Manager so that when the
- * user picks up an icon in the Workspace and drags it over our document window
- * and lets go, iconEntered:... and iconReleasedAt::ok: messages will be
- * sent to the DrawDocument from the Workspace Manager. Allows the user to
- * drag PostScript and TIFF files into the document.
- */
- - registerWindow
- {
- unsigned int windowNum;
- id speaker = [NXApp appSpeaker];
-
- listenerId = [Listener new];
- [listenerId setDelegate:self];
- [listenerId usePrivatePort];
- [listenerId addPort];
-
- NXConvertWinNumToGlobal([windowId windowNum], &windowNum);
- [speaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
- [speaker registerWindow:windowNum toPort:[listenerId listenPort]];
-
- return self;
- }
-
- /* Undoes what registerWindow does. */
- - unregisterWindow
- {
- unsigned int windowNum;
- id speaker = [NXApp appSpeaker];
-
- if (listenerId)
- {
- [speaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
- NXConvertWinNumToGlobal([windowId windowNum], &windowNum);
- [speaker unregisterWindow:windowNum];
- [listenerId free];
- }
-
- return self;
- }
-
- /*
- * Called whenever an icon is dragged from the Workspace over the document
- * window. At this point, all that is done is to salt away the list of files
- * represented by the icon. All the real work is done in iconReleasedAt::ok:.
- */
- - (int)iconEntered:(int)windowNum at:(double)x :(double)y
- iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
- iconWidth:(double)iconWidth iconHeight:(double)iconHeight
- pathList:(char *)pathList
- {
- if (!iconPathList || strcmp(iconPathList, pathList))
- {
- NX_FREE(iconPathList);
- NX_MALLOC(iconPathList, char, strlen(pathList)+1);
- strcpy(iconPathList, pathList);
- }
- return 0;
- }
-
- /*
- * Goes through the list of files associated with the icon dragged
- * from the Workspace and checks if any of them are PostScript or TIFF.
- * If any are, then the GraphicView is asked to load those in as objects.
- * Very important: an NX_DURING handler is required around all the processing
- * of this method since an uncaught raised error will cause this method not
- * to return and thus hang the Workspace Manager for a while.
- */
- - (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
- {
- volatile int foundOne = NO;
- char *file, *tab, *extension;
-
- int format;
-
- id importpanel;
-
- NXPoint pt;
-
- NX_DURING
- importpanel = [ImportPanel new];
- format = [importpanel format];
- [importpanel setFormat:IMPORT_COPY];
-
- pt.x = x;
- pt.y = y;
- [windowId convertScreenToBase:&pt];
- [drawingviewId convertPoint:&pt fromView:nil];
- file = iconPathList;
- while (file)
- {
- tab = strchr(file, '\t');
- if (tab)
- *tab = '\0';
- extension = strrchr(file, '.');
- if (extension)
- {
- if (!strcmp(extension, ".ps") ||
- !strcmp(extension, ".eps") ||
- !strcmp(extension, ".tiff"))
- if ([drawingviewId importFile:file at:&pt])
- foundOne = YES;
- }
- file = tab ? tab++ : NULL;
- }
- if (foundOne)
- {
- [NXApp activateSelf:YES];
- [windowId makeKeyAndOrderFront:self];
- }
- [importpanel setFormat:format];
- NX_HANDLER
- NX_ENDHANDLER
-
- *flag = foundOne;
-
- return 0;
- }
-
- /* Validates whether a menu command makes sense now */
-
- /*
- * Validates whether a menu command that DrawDocument responds to
- * is valid at the current time.
- */
- - (BOOL)validateCommand:menuCell
- {
- SEL action = [menuCell action];
-
- if (action == @selector(save:))
- return ([drawingviewId isDirty] || !saved);
- else if (action == @selector(saveTo:))
- return ([drawingviewId isSelected]);
- else if (action == @selector(revertToSaved:))
- return ([drawingviewId isDirty] && saved);
-
- return YES;
- }
-
- /* Cursor-setting method */
-
- /*
- * Resets the document's cursor rectangle to be the frame of the
- * drawing view.
- * Makes the drawing view the first responder if
- * there isn't one or if no tool is selected.
- */
- - resetResponder
- {
- id responder;
-
- responder = [windowId firstResponder];
- if (!responder || responder == windowId || [NXApp cursor] == NXArrow)
- [windowId makeFirstResponder:drawingviewId];
-
- return self;
- }
-
- @end
-
-